#pragma once

#include <iostream>
#include <string>
#include <cstring>
#include <functional>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "Log.hpp"
#include "Comm.hpp"
#include "InetAddr.hpp"
#include "ThreadPool_singleton.hpp"

static const uint16_t g_port = 8080;
static const int g_socket = -1;
static const int g_backlog = 8;

using task_t = std::function<void()>;

class EchoServer
{
private:
    void HandlerIO(int sockfd, InetAddr client)
    {
        char buffer[1024];
        while (true)
        {
            buffer[0] = 0;
            // 读取数据
            ssize_t n = read(sockfd, buffer, sizeof(buffer) - 1);
            if (n > 0)
            {
                buffer[n] = 0;
                std::string echo_str;
                echo_str += buffer;
                // 数据发送
                LOG(LogLevel::INFO) << client.ToString() << " say: " << buffer;
                write(sockfd, echo_str.c_str(), echo_str.size());
            }
            else if (n == 0)
            {
                LOG(LogLevel::INFO) << "client: " << client.ToString() << " quit, server quit too, close fd: " << sockfd;
                break;
            }
            else
            {
                LOG(LogLevel::WARING) << "read client: " << client.ToString() << "error, sockfe: " << sockfd;
                break;
            }
        }
        close(sockfd); // 关闭文件描述符
    }

public:
    EchoServer(uint16_t port = g_port)
        : _port(port),
          _listen_sockfd(g_socket)
    {
    }
    void Init()
    {
        // 创建套接字
        _listen_sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_listen_sockfd < 0)
        {
            LOG(LogLevel::FATAL) << "create listen socket error";
            exit(SOCKET_CREATE_ERR);
        }
        LOG(LogLevel::INFO) << "Create socket success: " << _listen_sockfd;

        // bind
        InetAddr local(_port);
        socklen_t len = local.Length();

        if (bind(_listen_sockfd, local.Addr(), local.Length()) != 0)
        {
            LOG(LogLevel::FATAL) << "bind socket error";
            exit(SOCKET_BIND_ERR);
        }

        // struct sockaddr_in local;
        // memset(&local, 0, sizeof(local));
        // local.sin_family = AF_INET;
        // local.sin_port = htonl(_port);
        // local.sin_addr.s_addr = INADDR_ANY;

        // if (bind(_listen_sockfd, (struct sockaddr *)&local, sizeof(local)) != 0)
        // {
        //     LOG(LogLevel::FATAL) << "bind socket error";
        //     exit(SOCKET_BIND_ERR);
        // }
        LOG(LogLevel::INFO) << "bind socket success: " << _listen_sockfd;

        // listen:连接
        // 第二个参数backlog：表示最大连接数-1,一般不建议设置太长
        // listen后服务器就已经开始启动了
        if (listen(_listen_sockfd, g_backlog) != 0)
        {
            LOG(LogLevel::FATAL) << "listen socket error";
            exit(SOCKET_LISTEN_ERR);
        }
        LOG(LogLevel::INFO) << "Listen socket success: " << _listen_sockfd;
    }

    static void *Routine(void *args)
    {
        ThreadDatas *td = static_cast<ThreadDatas *>(args);
        pthread_detach(pthread_self()); // 分离
        td->_self->HandlerIO(td->_sockfd, td->_addr);

        delete td;
        return nullptr;
    }

    class ThreadDatas
    {
    public:
        ThreadDatas(int sockfd, EchoServer *self, InetAddr &addr)
            : _sockfd(sockfd),
              _self(self),
              _addr(addr)
        {
        }

    public:
        int _sockfd;
        EchoServer *_self;
        InetAddr _addr;
    };

    void Start()
    {
        // signal(SIGCHLD, SIG_IGN);//最佳实践, 解决阻塞等待
        while (true)
        {
            sleep(1);
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            // 获取连接
            // accept:没有新连接, 返回-1, 默认阻塞; 有新连接, accept返回一个文件描述符, 获取新连接
            // socket_fd 和 accept_fd的区别
            // socket_fd用于获取新连接, accept用于服务新连接
            int sockfd = accept(_listen_sockfd, (struct sockaddr *)&peer, &len);
            if (sockfd < 0)
            {
                // 获取连接失败不属于错误,是正常情况
                LOG(LogLevel::WARING) << "accept client error";
                continue;
            }
            InetAddr client(peer);
            LOG(LogLevel::INFO) << "#Accept Client Success, Sockfd: " << sockfd << "Client Addr: " << client.ToString();
            // Handler

            // 方案一: 单进程,无法处理多个客户的同时请求
            // HandlerIO(sockfd, client);

            // 方案二: 多进程
            // 子进程会继承父进程的文件描述符表, 也就包含sockfd
            // pid_t id = fork(); // 创建之进程
            // if (id < 0)
            // {
            //     LOG(LogLevel::FATAL) << "资源不足, 创建紫禁城失败";
            //     exit(FORK_ERR);
            // }
            // else if (id == 0) // 子进程
            // {
            //     // 子进程在创建一个孙子进程
            //     // 关闭监听套接字, 防止子进程误操作，影响父进程使用监听套接字
            //     close(_listen_sockfd); // 子进程关闭监听套接字并不影响父进程使用套接字

            //     if (fork() > 0)
            //     {
            //         exit(OK); // 子进程直接退出,回到父进程
            //     }
            //     // 用该孙子进程来执行Handler方法
            //     // 该孙子进程会被系统领养, 成为孤儿进程
            //     HandlerIO(sockfd, client);
            //     exit(OK);
            // }
            // else
            // {
            //     // 父进程
            //     // 原因: 1、父进程不使用sockfd 2、避免sockfd浪费，造成sockfd泄露
            //     close(sockfd);
            //     pid_t rid = waitpid(id, nullptr, 0); // 这里用阻塞等待
            // }

            // 方案三: 多线程
            // 多线程共用一个文件描述符, 所以主线程和新线程不需要关闭sockfd和lisentsockfd
            // pthread_t tid;
            // ThreadDatas *td = new ThreadDatas(sockfd, this, client);
            // pthread_create(&tid, nullptr, Routine, (void *)td);

            // 多线程和多进程的缺点
            // 1、效率较低, 连接来了才进行进程/线程创建
            // 2、执行流个数没有上限

            // 方案四: 线程池
            ThreadPool<task_t>::GetInstance()->Push([this, sockfd, client]() {
                    this->HandlerIO(sockfd, client);
                }
            );
        }
    }
    ~EchoServer() {}

private:
    int _listen_sockfd; // 监听套接字
    uint16_t _port;
};